﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.Reflection;

namespace 双人对战游戏服务端
{
    public class ClientState
    {
        public Socket socket;
        public byte[] readBuff = new byte[1024];
        public int hp = -100;
        public float x = 0;
        public float y = 0;
        public float z = 0;
        public float eulY = 0;

    }

 
    internal class Program
    {
        //监听socket
        static Socket listenfd;
        public static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();

        static void Main(string[] args)
        {
            listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定
            listenfd.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888));

            listenfd.Listen(0);
            Console.WriteLine("[服务器]启动成功！");
            //CheckRead 使用select 多路复用 解决poll CPU占用率高的问题
            //同时检测多个Socket的状态，如果有一个或者多个Socket可读或可写或者发送错误，那么就返回这些可读的Socket
            //没有可读的Socket就阻塞。
            List<Socket> checkRead = new List<Socket>();
            //主循环 遍历checkRead 使用Select函数检测是否有返回可读socket
            while (true)
            {
                //填充CheckRead列表
                checkRead.Clear();
                checkRead.Add(listenfd);
                foreach (ClientState s in clients.Values)
                {
                    checkRead.Add(s.socket);
                }
                //Select
                Socket.Select(checkRead, null, null, 1000);
                //检查可读对象
                foreach (Socket s in checkRead)
                {
                    if (s == listenfd)
                    {
                        ReadListenfd(s);
                    }
                    else
                    {
                        ReadClientfd(s);
                    }
                }

            }

      
        }

  /*
        private static void AcceptCallBack(IAsyncResult ar)
        {
            try
            {
                Console.WriteLine("[服务器]Accept");
                //listenfd是把Main中原来的listenfd从回调函数的参数里拿出来
                Socket listenfd = (Socket)ar.AsyncState;
                //其实可以直接拿Socket clientfd = (Socket)ar.AsyncState.EndAccept(ar)
                Socket clientfd = listenfd.EndAccept(ar);
                //clients列表
                ClientState state = new ClientState();
                state.socket = clientfd;
                clients.Add(clientfd, state);
                //接收数据
                clientfd.BeginReceive(state.readBuff, 0, 1024, 0, ReceiveCallBack, state    );
                //继续Accept
                listenfd.BeginAccept(AcceptCallBack, listenfd);
            }
            catch(SocketException e) 
            {
                Console.WriteLine("Socket Accept Fail"+e.ToString());
            
            }
        }
*/

        //异步方法已淘汰
  /*      private static void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                Console.WriteLine("[服务器]接收成功");
                ClientState state = (ClientState)ar.AsyncState;

                Socket clientfd = state.socket;
                //关闭接收
                int count = clientfd.EndReceive(ar);

                if (count <= 0)
                {
                    clientfd.Close();
                    clients.Remove(clientfd);
                    Console.WriteLine("Socket Close");
                    return;
                }
                string recvStr = Encoding.Default.GetString(state.readBuff, 0, count);
                byte[] sendBytes = Encoding.Default.GetBytes("echo" + recvStr);
                clientfd.Send(sendBytes);
                clientfd.BeginReceive(state.readBuff, 0, 1024, 0, ReceiveCallBack, state);
            }
            catch (SocketException e)
            {
                Console.WriteLine("Socket Receive Fail"+e.ToString());
            }
        }
*/
        private static bool ReadClientfd(Socket clientfd)
        {
            ClientState state = clients[clientfd];
            int count = 0;
            try
            {
                count = clientfd.Receive(state.readBuff);
            }
            catch (SocketException e)
            {
                MethodInfo f = typeof(EventHandler).GetMethod("OnDisconnect");
                object[] ob = { state };
                f.Invoke(null, ob);

                clientfd.Close();
                clients.Remove(clientfd);
                Console.WriteLine("Receive Fail"+e.ToString());
                return false;
            }
            //客户端关闭
            if (count <= 0)
            {
                MethodInfo f = typeof(EventHandler).GetMethod("OnDisconnect");
                object[] ob = { state };
                f.Invoke(null, ob);

                clientfd.Close();
                clients.Remove(clientfd);
                Console.WriteLine("Client Close");
                return false;
            }
            //广播
            string recvStr = Encoding.Default.GetString(state.readBuff, 0, count);
            string[] split = recvStr.Split('|');

            Console.WriteLine("收到"+recvStr);
            string msgName = split[0];
            string msgArgs = split[1];
            string funNmae = "Msg" + msgName;
            MethodInfo mi = typeof(MsgHandler).GetMethod(funNmae);
            object[] o = { state, msgArgs };
            mi.Invoke(null, o);
            return true;

        }

        private static void ReadListenfd(Socket s)
        {
            Console.WriteLine("Accept");
            Socket clientfd = listenfd.Accept();
            ClientState state = new ClientState();
            state.socket = clientfd;
            clients.Add(clientfd,state);

        }

        public static void Send(ClientState cs, string sendStr)
        {
            try
            {
                Socket clientSocket = cs.socket;
                byte[] sendBytes = Encoding.Default.GetBytes(sendStr);
                clientSocket.Send(sendBytes);
            }
            catch(SocketException e)
            {
                Console.WriteLine(e.ToString());
            }
        }

    }

    
}
